package com.mybatis.jpa.SqlProvider;


import com.mybatis.jpa.annotation.Column;
import com.mybatis.jpa.annotation.Id;
import com.mybatis.jpa.annotation.Table;
import com.mybatis.jpa.meta.MetaCell;
import org.apache.ibatis.builder.annotation.ProviderContext;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author lgt
 * @Date 2017/10/9 : 14:46
 */
public class ProviderUtil {

    private static Map<String, MetaCell> TABLE_META_CACHE = new HashMap<>();

    public static String getFieldString(MetaCell meta) {
        StringBuilder sb = new StringBuilder(" ");
        String[] fields = meta.getFields();
        String[] columns = meta.getColumns();
        for (int i =0 ; i<fields.length-1;i++) {
            sb.append(columns[i]).append(" ");
            if (!Objects.equals(fields[i], columns[i])) {
                sb.append(fields[i]);
            }
            sb.append(",");
        }
        sb.append(fields[fields.length-1]).append(" ");
        if (!Objects.equals(fields[fields.length-1], columns[fields.length-1])){
            sb.append(columns[fields.length-1]);
        }
        return sb.toString();
    }

    public static MetaCell getMetaCellByMapper(ProviderContext context){
        Class<?> mapper = context.getMapperType();
        return getMetaCellByMapper(mapper);
    }

    public static MetaCell getMetaCellByMapper(Class<?> mapper) {
        if (TABLE_META_CACHE.containsKey(mapper.getName())) {
            return TABLE_META_CACHE.get(mapper.getName());
        } else {
            MetaCell metaCell = createMetaCell(mapper);
            TABLE_META_CACHE.put(mapper.getName(), metaCell);
            return metaCell;
        }
    }

    private static MetaCell createMetaCell(Class<?> mapper) {
        Check.isTrue(mapper.getGenericInterfaces().length>0,"mapper must extents interface Repository<T,K>");
        Type repoType = mapper.getGenericInterfaces()[0];
        Type[] actualTypeArguments = ((ParameterizedType) repoType).getActualTypeArguments();
        Check.isTrue(actualTypeArguments.length == 2, "extends interface Repository must declare generic");
        Class<?> metaClass = (Class<?>) actualTypeArguments[0];
        MetaCell meta = new MetaCell();
        meta.setMapperName(mapper.getName());
        Table table = metaClass.getAnnotation(Table.class);
        Check.notNull(table,"meta annotation must have Table");
        meta.setTableName(table.value());
        Field[] fields = Arrays.stream(metaClass.getDeclaredFields()).filter(field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal
                (field.getModifiers()))).toArray(Field[] :: new);
        String[] fieldsArr = new String[fields.length];
        String[] columns = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldsArr[i] = fields[i].getName();
            Column column = fields[i].getAnnotation(Column.class);
            if (Objects.nonNull(column)) {
                columns[i] = column.value();
            }
            Id id =fields[i].getAnnotation(Id.class);
            if (Objects.nonNull(id)) {
                if ("".equals(id.value())) {
                    columns[i] = fields[i].getName();
                    meta.setPrimaryKey(fields[i].getName());
                } else {
                    columns[i] = id.value();
                    meta.setPrimaryKey(id.value());
                }
            }
            if (Objects.isNull(columns[i])) {
                columns[i] = fields[i].getName();
            }
        }
        if (fields.length == 0) {
            throw new RuntimeException(mapper.getName() + " must contain fields ");
        }
        meta.setFields(fieldsArr);
        meta.setColumns(columns);
        return meta;
    }

    public static String getKeyProperty(Class<?> mapper) {
        MetaCell meta = getMetaCellByMapper(mapper);
        if (Objects.nonNull(meta.getPrimaryKey())) {
            for (int i = 0; i < meta.getColumns().length; i++) {
                if (Objects.equals(meta.getPrimaryKey(), meta.getColumns()[i])) {
                    return meta.getFields()[i];
                }
            }
        }
        return "id";
    }
}
